#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <dirent.h>

#include "updater.types.h"
#include "updater.w.h"
#include "updater.w.string.h"
#include "updater.w.memory.h"

/* Get a variable x, flag to test y, flag to set z */
/* If the flag y present, then set flag z to x2*/
#define TEST_AND_SET(x1,flag,x2) do{ if(x1 & W_##flag){x2 |= flag;} }while(0)

/* Return the file desriptor. */
INT w_extract_fd(FD_T * ps_fd)
{
    RETURN( ps_fd->fd);
}

INT w_filestatus(FD_T * ps_fd)
{
    RETURN( w_extract_fd( ps_fd ) );
}


FD_T * w_open(CHAR * pc_filename, INT i_flags)
{
    FD_T * ps_fd;
    INT i_real_flags = 0;
    ps_fd = w_malloc (sizeof(FD_T));
    if (NULL == ps_fd)
    {
        return NULL;
    }

    /* Read flags and assign real flags */

    TEST_AND_SET(i_flags, O_WRONLY, i_real_flags);
    TEST_AND_SET(i_flags, O_RDWR, i_real_flags);
    TEST_AND_SET(i_flags, O_ACCMODE, i_real_flags);
    TEST_AND_SET(i_flags, O_EXCL, i_real_flags);
    TEST_AND_SET(i_flags, O_NOCTTY, i_real_flags);
    TEST_AND_SET(i_flags, O_TRUNC, i_real_flags);
    TEST_AND_SET(i_flags, O_APPEND, i_real_flags);
    TEST_AND_SET(i_flags, O_NONBLOCK, i_real_flags);
    TEST_AND_SET(i_flags, O_NDELAY, i_real_flags);
    TEST_AND_SET(i_flags, O_FSYNC, i_real_flags );
    TEST_AND_SET(i_flags, O_ASYNC, i_real_flags );


    ps_fd->fd = open(pc_filename, i_real_flags);
    RETURN( ps_fd );
}

INT w_read(FD_T * ps_fd, CHAR * pc_buf, size_t i_size)
{
    RETURN( read( w_extract_fd( ps_fd ), pc_buf, i_size) );
}

INT w_write(FD_T * ps_fd, CHAR * pc_buf, size_t i_size)
{
    RETURN( write( w_extract_fd( ps_fd ), pc_buf, i_size) );
}


OFF_T w_lseek(FD_T * ps_fd, size_t i_offset, INT i_whence)
{
    INT i_real_flags = 0;

    TEST_AND_SET(i_whence, SEEK_CUR, i_real_flags);
    TEST_AND_SET(i_whence, SEEK_END, i_real_flags);

    RETURN( lseek( w_extract_fd( ps_fd ), i_offset, i_real_flags ) );
}

INT w_close(FD_T * ps_fd)
{
    close( w_extract_fd( ps_fd ) );
    w_free( ps_fd );
    RETURN(0);
}

INT w_stat(const CHAR * pc_path, STAT_T * ps_st)
{
    if ( NULL == ps_st )
    {
        RETURN(-1);
    }

    RETURN( stat(pc_path, (struct stat *)ps_st->stat ) );
}

FD_T * w_mkstemp( CHAR * pc_buf)
{
    FD_T * ps_fd = w_malloc(sizeof(FD_T));
    if ( NULL == ps_fd )
        RETURN( ps_fd );
    ps_fd->fd = (INT) w_mkstemp(pc_buf);
    RETURN( ps_fd );
}

VOID * w_opendir(const CHAR * pc_name)
{
    VOID * pv_ret;
    pv_ret = opendir(pc_name);
    RETURN( pv_ret );
}

INT w_closedir(VOID * pv_dir)
{
    INT i_ret;
    i_ret = closedir ( pv_dir );
    RETURN( i_ret );
}


INT w_mkdir(const CHAR * pc_pathname, MODE_T i_mode)
{
    INT i_ret;
    MODE_T i_real_flags = 0;

    TEST_AND_SET(i_mode, S_IFMT, i_real_flags);
    TEST_AND_SET(i_mode, S_IFSOCK, i_real_flags);


    i_ret = mkdir(pc_pathname,i_mode);
    RETURN(i_ret);
}


INT w_unlink( const CHAR * pc_name)
{
    INT i_rv;
    i_rv = unlink(pc_name);
    RETURN(i_rv);
}


STAT_T * w_new_stat(void)
{
    STAT_T * ps_stat;
    ps_stat = w_malloc(sizeof(STAT_T));
    if (NULL == ps_stat)
    {
        RETURN(NULL);
    }

    ps_stat->stat = w_malloc(sizeof(struct stat));
    if (NULL == ps_stat->stat)
    {
        RETURN(NULL);
    }

    RETURN(ps_stat);

}


INT w_free_stat(STAT_T * ps_stat)
{
    if (NULL != ps_stat)
    {
        if (NULL != ps_stat->stat)
            w_free(ps_stat->stat);

        w_free(ps_stat);
    }
    RETURN(0);
}


/* protection */

MODE_T w_stat_mode(STAT_T * ps_stat)
{
    MODE_T i_mode ;
    if (NULL == ps_stat || NULL == ps_stat->stat)
    {
        RETURN(-1);
    }

    i_mode =  ((struct stat *)ps_stat->stat)->st_mode;
    RETURN( i_mode );
}


/* total size, in bytes */

OFF_T w_stat_size(STAT_T * ps_stat)
{
    if (NULL == ps_stat || NULL == ps_stat->stat )
    {
        RETURN(-1);
    }
    RETURN(((struct stat *) ps_stat->stat)->st_size);
}


/* number of hard links */

UINT w_stat_links(STAT_T * ps_stat)
{
    if (NULL == ps_stat || NULL == ps_stat->stat)
    {
        RETURN(-1);
    }
    RETURN(((struct stat *) ps_stat->stat)->st_nlink);
}

/* user ID of owner */

UID_T w_stat_user(STAT_T * ps_stat)
{
    if (NULL == ps_stat || NULL == ps_stat->stat)
    {
        RETURN(-1);
    }
    RETURN(((struct stat *) ps_stat->stat)->st_uid);
}


/* group ID of owner */

MODE_T w_stat_group(STAT_T * ps_stat)
{
    if (NULL == ps_stat || NULL == ps_stat->stat)
    {
        RETURN(-1);
    }
    RETURN(((struct stat *) ps_stat->stat)->st_gid);
}


/* blocksize for filesystem I/O */

INT w_stat_blocksize(STAT_T * ps_stat)
{
    if (NULL == ps_stat || NULL == ps_stat->stat)
    {
        RETURN(-1);
    }
    RETURN(((struct stat *) ps_stat->stat)->st_blksize);
}


/* number of blocks allocated */

INT w_stat_blocks_allocated(STAT_T * ps_stat)
{
    if (NULL == ps_stat || NULL == ps_stat->stat)
    {
        RETURN(-1);
    }
    RETURN(((struct stat *) ps_stat->stat)->st_blocks);
}


/* time of last access */

TIME_T w_stat_time_last_access(STAT_T * ps_stat)
{
    if (NULL == ps_stat || NULL == ps_stat->stat)
    {
        RETURN(-1);
    }
    RETURN(((struct stat *) ps_stat->stat)->st_atime);
}


/* time of last modification */

TIME_T w_stat_time_last_modification(STAT_T * ps_stat)
{
    if (NULL == ps_stat || NULL == ps_stat->stat)
    {
        RETURN(-1);
    }
    RETURN(((struct stat *) ps_stat->stat)->st_mtime);
}


/* time of last status change */

TIME_T w_stat_time_last_change(STAT_T * ps_stat)
{
    if (NULL == ps_stat || NULL == ps_stat->stat)
    {
        RETURN(-1);
    }

    RETURN(((struct stat *) ps_stat->stat)->st_ctime);
}





INT w_stat_isreg(STAT_T * ps_stat)
{
    struct stat s_stat;
    if (NULL == ps_stat || NULL == ps_stat->stat)
    {
        RETURN(-1);
    }

    s_stat = *(struct stat *) ps_stat->stat;
    RETURN(S_ISREG(  s_stat.st_mode ) ) ;
}


INT w_stat_isdir(STAT_T * ps_stat)
{
    struct stat s_stat;
    if (NULL == ps_stat || NULL == ps_stat->stat)
    {
        RETURN(-1);
    }
    s_stat = *(struct stat *) ps_stat->stat;
    RETURN( S_ISDIR(s_stat.st_mode) );
}


INT w_stat_ischr(STAT_T * ps_stat)
{
    struct stat s_stat;
    if (NULL == ps_stat || NULL == ps_stat->stat)
    {
        RETURN(-1);
    }
    s_stat = *(struct stat *) ps_stat->stat;
    RETURN( S_ISCHR(s_stat.st_mode) );
}


INT w_stat_isblk(STAT_T * ps_stat)
{
    struct stat s_stat;
    if (NULL == ps_stat || NULL == ps_stat->stat)
    {
        RETURN(-1);
    }
    s_stat = *(struct stat *) ps_stat->stat;
    RETURN( S_ISBLK(s_stat.st_mode) );
}


INT w_stat_isfifo(STAT_T * ps_stat)
{
    struct stat s_stat;
    if (NULL == ps_stat || NULL == ps_stat->stat)
    {
        RETURN(-1);
    }
    s_stat = *(struct stat *) ps_stat->stat;
    RETURN( S_ISFIFO(s_stat.st_mode) );
}


INT w_stat_issock(STAT_T * ps_stat)
{
    struct stat s_stat;
    if (NULL == ps_stat || NULL == ps_stat->stat)
    {
        RETURN(-1);
    }
    s_stat = *(struct stat *) ps_stat->stat;
    RETURN( S_ISSOCK(s_stat.st_mode) );
}



